/**
 * \file ArrayBase.h
 * \brief ArrayBase definition and declaration class
 * \author Edouard ROGE
 * \date 13 september 2012
 */
#ifndef _ARRAY_BASE_
#define _ARRAY_BASE_

#include <stdlib.h>
#include <cstring>
#include "Core/Define/Define.h"
#include "Core/Memory/MemoryPoolManager.h"
#include "Core/Thread/Mutex.h"
#include "Core/Log/LogManager.h"

/**
 * \class ArrayBase
 * \brief base class for all container
 */

template <class T>
class ArrayBase
{
public:

	/**
	 * \fn ArrayBase();
	 * \brief Default constructor
	 */
	ArrayBase();

	/**
	 * \fn ArrayBase(uint32 _iSize);
	 * \param _iSize new array's size
	 * \brief constructor, Resize array with the size in param
	 */
	ArrayBase(uint32 _iSize);

	/**
	 * \fn virtual ~ArrayBase();
	 * \brief Destructor
	 */
	virtual ~ArrayBase();

	/**
	 * \fn uint32 Size() const;
	 * \return array's size
	 * \brief array's size (number of element)
	 */
	uint32 Size() const;

	/**
	 * \fn uint32 Capacity() const;
	 * \return array's capacity
	 * \brief array's capacity (number of element allocated)
	 */
	uint32 Capacity() const;

	/**
	 * \fn void Reserve(uint32 _iSize);
	 * \param _iSize size to reserve
	 * \brief reserve specified size for the array
	 */
	void Reserve(uint32 _iSize);

	/**
	 * \fn void Resize(uint32 _iSize);
	 * \param _iSize new array size
	 * \brief resize array
	 */
	void Resize(uint32 _iSize);

	/**
	 * \fn void Reset();
	 * \brief Reset array size to zero
	 */
	void Reset();

	/**
	 * \fn void Clear();
	 * \brief Clear the array, and set the capacity to zero
	 */
	void Clear();

	/**
	 * \fn operator T * ();
	 * \brief casting operator
	 */
	operator T * ();

	/**
	 * \fn operator const T * ()const;
	 * \brief casting operator
	 */
	operator const T * ()const;

protected:
	/**
	 * \fn void _Insert(T * _iter, const T & _element);
	 * \param _iter place where the element is inserted
	 * \param _element element to insert
	 * \brief insert an element to the position given by the iterator
	 */
	void _Insert(T * _iter, const T & _element);

	/**
	 * \fn Iterator _Remove(T * _it);
	 * \param _it place to remove an element
	 * \return a pointer to the next element
	 * \brief remove an element from the array
	 */
	T * _Remove(T * _it);

	/**
	 * \fn void _Insert(const T * _it, const T & _element);
	 * \param _it place where the element is inserted
	 * \param _element element to insert
	 * \brief insert an element to the position given by the iterator
	 */
	void _Insert(const T *  _it, const T & _element);

	/**
	 * \fn const T * _Remove(const T * _it);
	 * \param _it place to remove an element
	 * \return a pointer to the next element
	 * \brief remove an element from the array
	 */
	const T * _Remove(const T * _it);

	/**
	 * \fn T * _Find(const T & _element);
	 * \param _element element to find
	 * \return iterator pointing on this element if find, otherwise returns end iterator
	 * \brief find element in array
	 */
	T * _Find(const T & _element);

	/**
	 * \fn const T * _Find(const T & _element)const;
	 * \param _element element to find
	 * \return iterator pointing on this element if find, otherwise returns end iterator
	 * \brief find element in array
	 */
	const T * _Find(const T & _element)const;

	/**
	 * \fn void _Clone(const T * _tArrayBase, uint32 _iSize);
	 * \param _tArrayBase array to copy
	 * \param _iSize array's size to copy
	 * \brief Copy _tArrayBase's values
	 */
	void _Clone(const T * _tArrayBase, uint32 _iSize);

	/** Array's size*/
	uint32 m_iSize;

	/** Array's capacity */
	uint32 m_iCapacity;

	/** Array's values */
	T * m_tArray;
};

template< class T >
ArrayBase<T>::ArrayBase()
: m_iSize(0), m_iCapacity(0), m_tArray(NULL)
{
}

template< class T >
ArrayBase<T>::ArrayBase(uint32 _iSize)
: m_iSize(0), m_iCapacity(0), m_tArray(NULL)
{
	Resize(_iSize);
}

template< class T >
ArrayBase<T>::~ArrayBase()
{
	Clear();
}

template< class T >
void ArrayBase<T>::_Clone(const T * _tArray, uint32 _iSize)
{
	Clear();

	m_iSize = _iSize;
	m_iCapacity = _iSize;
	m_tArray = (T*)MemoryPoolManager::GetInstance().Allocate(_iSize * sizeof(T));
	for(uint32 i = 0; i < _iSize; ++i)
		new(m_tArray+i)T();
	memcpy(m_tArray, _tArray, m_iSize * sizeof(T));

}

template< class T >
uint32 ArrayBase<T>::Size() const
{
	return m_iSize;
}

template< class T >
uint32 ArrayBase<T>::Capacity() const
{
	return m_iCapacity;
}

template< class T >
void ArrayBase<T>::Reserve(uint32 _iSize)
{

	if( ! m_tArray )
	{
		m_tArray = (T*)MemoryPoolManager::GetInstance().Allocate(_iSize * sizeof(T));
		for(uint32 i = 0; i < _iSize; ++i)
			new(m_tArray+i)T;
		m_iCapacity = _iSize;
	}
	else if( m_iCapacity < _iSize )
	{

		if( m_iSize == 0 )
		{
			MemoryPoolManager::GetInstance().Free(m_tArray);
			m_tArray = (T*)MemoryPoolManager::GetInstance().Allocate(_iSize * sizeof(T));
			for(uint32 i = 0; i < _iSize; ++i)
				new(m_tArray+i)T;
		}
		else
		{
			T * tArrayTemp = (T*)MemoryPoolManager::GetInstance().Allocate(_iSize * sizeof(T));
			for(uint32 i = 0; i < _iSize; ++i)
				new(tArrayTemp+i)T;

			memcpy(tArrayTemp, m_tArray, sizeof(T) * m_iSize );
			MemoryPoolManager::GetInstance().Free(m_tArray);
			m_tArray = tArrayTemp;
		}
		m_iCapacity = _iSize;
	}

}

template< class T >
void ArrayBase<T>::Resize(uint32 _iSize)
{
	m_iSize = _iSize;
	if( m_iCapacity < m_iSize )
		Reserve(m_iSize);
}

template< class T >
void ArrayBase<T>::Reset()
{

	for(uint32 i = 0; i < m_iSize; ++i)
			m_tArray[i].~T();
	m_iSize = 0;

}

template< class T >
void ArrayBase<T>::Clear()
{

	for(uint32 i = 0; i < m_iSize; ++i)
		m_tArray[i].~T();

	m_iSize = 0;
	m_iCapacity = 0;
	if(m_tArray)
	{
		MemoryPoolManager::GetInstance().Free(m_tArray);
		m_tArray = NULL;
	}

}

template< class T >
void ArrayBase<T>::_Insert(T * _iter, const T & _element)
{
	uint32 iPos = _iter - m_tArray;

	if( m_iCapacity == 0 )
	{
		m_iCapacity = 1;
		m_tArray = (T*)MemoryPoolManager::GetInstance().Allocate(1 * sizeof(T));
		new(m_tArray)T();
	}
	else if( m_iCapacity == m_iSize )
	{
		m_iCapacity = m_iSize*2;
		T * tArrayTemp = (T*)MemoryPoolManager::GetInstance().Allocate(m_iCapacity * sizeof(T));
		for(uint32 i = 0; i < m_iCapacity; ++i)
			new(tArrayTemp+i)T();

		memcpy(tArrayTemp, m_tArray, sizeof(T) * m_iSize );
		MemoryPoolManager::GetInstance().Free(m_tArray);
		m_tArray = tArrayTemp;
	}

	if( iPos < m_iSize )
	{
		for(uint32 i = m_iSize; i>iPos; --i)
			m_tArray[i] = m_tArray[i-1];
	}
	m_tArray[iPos] = _element;

	++m_iSize;

}

template< class T >
T * ArrayBase<T>::_Remove(T * _iter)
{

	uint32 iPos=_iter - m_tArray;
	if( iPos + 1 < m_iSize )
	{
		m_tArray[iPos].~T();
		uint32 iMoveSize = m_iSize - iPos - 1;
		memcpy(m_tArray + iPos, m_tArray + iPos + 1, sizeof(T) * iMoveSize );
	}

	--m_iSize;


	return m_tArray+iPos;
}

template< class T >
void ArrayBase<T>::_Insert(const T * _iter, const T & _element)
{
	uint32 iPos = _iter - m_tArray;

	if( m_iCapacity == 0 )
	{
		m_iCapacity = 1;
		m_tArray = (T*)MemoryPoolManager::GetInstance().Allocate(1 * sizeof(T));
		new(m_tArray)T();
	}
	else if( m_iCapacity == m_iSize )
	{
		m_iCapacity = m_iSize*2;
		T * tArrayTemp = (T*)MemoryPoolManager::GetInstance().Allocate(m_iCapacity * sizeof(T));
		for(uint32 i = 0; i < m_iCapacity; ++i)
			new(tArrayTemp+i)T();

		memcpy(tArrayTemp, m_tArray, sizeof(T) * m_iSize );
		MemoryPoolManager::GetInstance().Free(m_tArray);
		m_tArray = tArrayTemp;
	}

	if( iPos < m_iSize )
	{
		for(uint32 i = m_iSize; i>iPos; --i)
			m_tArray[i] = m_tArray[i-1];
	}
	m_tArray[iPos] = _element;

	++m_iSize;

}

template< class T >
const T * ArrayBase<T>::_Remove(const T * _iter)
{

	uint32 iPos=_iter - m_tArray;
	if( iPos + 1 < m_iSize )
	{
		m_tArray[iPos].~T();
		uint32 iMoveSize = m_iSize - iPos - 1;
		memcpy(m_tArray + iPos, m_tArray + iPos + 1, sizeof(T) * iMoveSize );
	}

	--m_iSize;


	return m_tArray+iPos;
}

template< class T >
T * ArrayBase<T>::_Find(const T & _element)
{
	for(uint32 i=0; i < m_iSize; ++i)
		if( m_tArray[i] == _element )
			return (m_tArray + i);

	return m_tArray + m_iSize;
}

template< class T >
const T * ArrayBase<T>::_Find(const T & _element)const
{
	for(uint32 i=0; i < m_iSize; ++i)
		if( m_tArray[i] == _element )
			return (m_tArray + i);

	return m_tArray + m_iSize;
}

template< class T >
ArrayBase<T>::operator T * ()
{
	return m_tArray;
}

template< class T >
ArrayBase<T>::operator const T * ()const
{
	return m_tArray;
}

#endif /* _ARRAY_BASE_ */
